{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "91\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\Administrator\\AppData\\Local\\Temp\\ipykernel_1768\\2937555138.py:32: LangChainDeprecationWarning: The class `Chroma` was deprecated in LangChain 0.2.9 and will be removed in 1.0. An updated version of the class exists in the :class:`~langchain-chroma package and should be used instead. To use it run `pip install -U :class:`~langchain-chroma` and import as `from :class:`~langchain_chroma import Chroma``.\n",
      "  vectorstore = Chroma(\n",
      "D:\\ProgramData\\anaconda3\\envs\\pytorch\\lib\\site-packages\\transformers\\models\\bert\\modeling_bert.py:439: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\\actions-runner\\_work\\pytorch\\pytorch\\builder\\windows\\pytorch\\aten\\src\\ATen\\native\\transformers\\cuda\\sdp_utils.cpp:455.)\n",
      "  attn_output = torch.nn.functional.scaled_dot_product_attention(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "finish\n"
     ]
    }
   ],
   "source": [
    "### Build Index\n",
    "\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_community.vectorstores import Chroma\n",
    "from customize.embedding import get_embedding\n",
    "\n",
    "### from langchain_cohere import CohereEmbeddings\n",
    "\n",
    "# Set embeddings\n",
    "embd = get_embedding(model_path=r\"F:\\models\\BAAI\\bge-large-en-v1.5\", lang=\"en\")\n",
    "\n",
    "# Docs to index\n",
    "urls = [\n",
    "    \"http://localhost:8000/lilianweng.github.io/lilianweng.github.io/posts/2023-06-23-agent/index.html\",\n",
    "    \"http://localhost:8000/lilianweng.github.io/lilianweng.github.io/posts/2023-03-15-prompt-engineering/index.html\",\n",
    "    \"http://localhost:8000/lilianweng.github.io/lilianweng.github.io/posts/2023-10-25-adv-attack-llm/index.html\",\n",
    "]\n",
    "\n",
    "# Load\n",
    "docs = [WebBaseLoader(url).load() for url in urls]\n",
    "docs_list = [item for sublist in docs for item in sublist]\n",
    "\n",
    "# Split\n",
    "text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(\n",
    "    chunk_size=500, chunk_overlap=50\n",
    ")\n",
    "\n",
    "doc_splits = text_splitter.split_documents(docs_list)\n",
    "print(len(doc_splits))\n",
    "persist_directory = 'db'\n",
    "vectorstore = Chroma(\n",
    "    collection_name=\"rag-chroma_new\",\n",
    "    embedding_function=embd,\n",
    "    persist_directory=persist_directory,\n",
    "    collection_metadata={\"hnsw:batch_size\": 150}\n",
    ")\n",
    "# # Add to vectorstore\n",
    "# vectorstore = Chroma.from_documents(\n",
    "#     documents=doc_splits,\n",
    "#     collection_name=\"rag-chroma_new\",\n",
    "#     embedding=embd,\n",
    "#     collection_metadata={\"hnsw:batch_size\": 10000},\n",
    "# )\n",
    "for doc in doc_splits:\n",
    "    vectorstore.add_documents([doc])\n",
    "print(\"finish\")\n",
    "retriever = vectorstore.as_retriever()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "outputs": [],
   "source": [
    "from langchain_chroma import Chroma\n",
    "from customize.embedding import get_embedding\n",
    "persist_directory = 'db'\n",
    "embd = get_embedding(model_path=r\"F:\\models\\BAAI\\bge-large-en-v1.5\", lang=\"en\")\n",
    "vectorstore = Chroma(\n",
    "    collection_name=\"rag-chroma_new\",\n",
    "    embedding_function=embd,\n",
    "    persist_directory=persist_directory,\n",
    "    collection_metadata={\"hnsw:batch_size\": 150}\n",
    ")\n",
    "retriever = vectorstore.as_retriever()"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "datasource='web_search'\n",
      "datasource='vectorstore'\n"
     ]
    }
   ],
   "source": [
    "### Router\n",
    "\n",
    "from typing import Literal\n",
    "\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from get_ollama import GetOllama\n",
    "\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "\n",
    "# Data model\n",
    "class RouteQuery(BaseModel):\n",
    "    \"\"\"Route a user query to the most relevant datasource.\"\"\"\n",
    "\n",
    "    datasource: Literal[\"vectorstore\", \"web_search\"] = Field(\n",
    "        ...,\n",
    "        description=\"Given a user question choose to route it to web search or a vectorstore.\",\n",
    "    )\n",
    "\n",
    "\n",
    "# LLM with function call\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "structured_llm_router = llm.with_structured_output(RouteQuery)\n",
    "# 您是将用户问题路由到矢量商店或网络搜索的专家。\n",
    "# vectorstore 包含与代理、提示工程和对抗性攻击相关的文档。\n",
    "# 使用 vectorstore 获取有关这些主题的问题。否则，使用网络搜索。\n",
    "# Prompt\n",
    "system = \"\"\"You are an expert at routing a user question to a vectorstore or web search.\n",
    "The vectorstore contains documents related to agents, prompt engineering, and adversarial attacks.\n",
    "Use the vectorstore for questions on these topics. Otherwise, use web-search.\"\"\"\n",
    "route_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "question_router = route_prompt | structured_llm_router\n",
    "print(\n",
    "    question_router.invoke(\n",
    "        {\"question\": \"Who will the Bears draft first in the NFL draft?\"}\n",
    "    )\n",
    ")\n",
    "print(question_router.invoke({\"question\": \"What are the types of agent memory?\"}))"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\ProgramData\\anaconda3\\envs\\pytorch\\lib\\site-packages\\transformers\\models\\bert\\modeling_bert.py:439: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\\actions-runner\\_work\\pytorch\\pytorch\\builder\\windows\\pytorch\\aten\\src\\ATen\\native\\transformers\\cuda\\sdp_utils.cpp:455.)\n",
      "  attn_output = torch.nn.functional.scaled_dot_product_attention(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "binary_score='no'\n"
     ]
    }
   ],
   "source": [
    "### Retrieval Grader\n",
    "\n",
    "\n",
    "# Data model(对检索到的文档进行相关性检查的二分类。)\n",
    "class GradeDocuments(BaseModel):\n",
    "    \"\"\"Binary score for relevance check on retrieved documents.\"\"\"\n",
    "\n",
    "    binary_score: str = Field(\n",
    "        description=\"Documents are relevant to the question, 'yes' or 'no'\"\n",
    "    )\n",
    "\n",
    "\n",
    "# LLM with function call\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "structured_llm_grader = llm.with_structured_output(GradeDocuments)\n",
    "# 您是评估检索到的文档与用户问题的相关性的评分者。\\n\n",
    "# 如果文档包含与用户问题相关的关键字或语义，请将其评为相关。\\n\n",
    "# 它不需要是严格的测试。目标是过滤掉错误的检索。\\n\n",
    "# 给出一个二进制分数 “是” 或 “否” 分数，以指示文档是否与问题相关。\n",
    "# Prompt\n",
    "system = \"\"\"You are a grader assessing relevance of a retrieved document to a user question. \\n\n",
    "    If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. \\n\n",
    "    It does not need to be a stringent test. The goal is to filter out erroneous retrievals. \\n\n",
    "    Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.\"\"\"\n",
    "grade_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        (\"human\", \"Retrieved document: \\n\\n {document} \\n\\n User question: {question}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "retrieval_grader = grade_prompt | structured_llm_grader\n",
    "question = \"agent memory\"\n",
    "docs = retriever.invoke(question)\n",
    "doc_txt = docs[1].page_content\n",
    "print(retrieval_grader.invoke({\"question\": question, \"document\": doc_txt}))"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['context', 'question'] input_types={} partial_variables={} metadata={'lc_hub_owner': 'rlm', 'lc_hub_repo': 'rag-prompt', 'lc_hub_commit_hash': '50442af133e61576e74536c6556cefe1fac147cad032f4377b60c436e6cdcb6e'} messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['context', 'question'], input_types={}, partial_variables={}, template=\"You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.\\nQuestion: {question} \\nContext: {context} \\nAnswer:\"), additional_kwargs={})]\n",
      "Agent memory in an LLM-powered autonomous agent system includes both short-term and long-term memory. Short-term memory is used for in-context learning, while long-term memory allows the agent to retain and recall information over extended periods, often through external vector stores and fast retrieval mechanisms.\n"
     ]
    }
   ],
   "source": [
    "from langchain import hub\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "# Prompt\n",
    "prompt = hub.pull(\"rlm/rag-prompt\")\n",
    "print(prompt)\n",
    "# LLM\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "\n",
    "\n",
    "# Post-processing\n",
    "def format_docs(docs):\n",
    "    return \"\\n\\n\".join(doc.page_content for doc in docs)\n",
    "\n",
    "\n",
    "# Chain\n",
    "rag_chain = prompt | llm | StrOutputParser()\n",
    "\n",
    "# Run\n",
    "generation = rag_chain.invoke({\"context\": docs, \"question\": question})\n",
    "print(generation)"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "outputs": [
    {
     "data": {
      "text/plain": "GradeHallucinations(binary_score='yes')"
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### Hallucination Grader\n",
    "\n",
    "\n",
    "# Data model\n",
    "class GradeHallucinations(BaseModel):\n",
    "    \"\"\"Binary score for hallucination present in generation answer.\"\"\"\n",
    "\n",
    "    binary_score: str = Field(\n",
    "        description=\"Answer is grounded in the facts, 'yes' or 'no'\"\n",
    "    )\n",
    "\n",
    "\n",
    "# LLM with function call\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "structured_llm_grader = llm.with_structured_output(GradeHallucinations)\n",
    "\n",
    "# 您是评估 LLM 生成是否基于 / 由一组检索到的事实支持的评分者。\\n\n",
    "# 给出 “是” 或 “否” 的二进制分数。“是” 意味着答案基于 / 由一组事实支持。\n",
    "\n",
    "# Prompt\n",
    "system = \"\"\"You are a grader assessing whether an LLM generation is grounded in / supported by a set of retrieved facts. \\n\n",
    "     Give a binary score 'yes' or 'no'. 'Yes' means that the answer is grounded in / supported by the set of facts.\"\"\"\n",
    "hallucination_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        (\"human\", \"Set of facts: \\n\\n {documents} \\n\\n LLM generation: {generation}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "hallucination_grader = hallucination_prompt | structured_llm_grader\n",
    "hallucination_grader.invoke({\"documents\": docs, \"generation\": generation})"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [
    {
     "data": {
      "text/plain": "GradeAnswer(binary_score='yes')"
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### Answer Grader\n",
    "\n",
    "\n",
    "# Data model\n",
    "class GradeAnswer(BaseModel):\n",
    "    \"\"\"Binary score to assess answer addresses question.\"\"\"\n",
    "\n",
    "    binary_score: str = Field(\n",
    "        description=\"Answer addresses the question, 'yes' or 'no'\"\n",
    "    )\n",
    "\n",
    "\n",
    "# LLM with function call\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "structured_llm_grader = llm.with_structured_output(GradeAnswer)\n",
    "\n",
    "# Prompt\n",
    "system = \"\"\"You are a grader assessing whether an answer addresses / resolves a question \\n\n",
    "     Give a binary score 'yes' or 'no'. Yes' means that the answer resolves the question.\"\"\"\n",
    "answer_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        (\"human\", \"User question: \\n\\n {question} \\n\\n LLM generation: {generation}\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "answer_grader = answer_prompt | structured_llm_grader\n",
    "answer_grader.invoke({\"question\": question, \"generation\": generation})"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [
    {
     "data": {
      "text/plain": "'What information or details does the agent need to remember or store?'"
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### Question Re-writer\n",
    "\n",
    "# LLM\n",
    "llm = GetOllama(model_type=1, temperature=0)()\n",
    "# 你是一个问题重写器，可以将输入问题转换为优化的更好版本 \\n\n",
    "# 用于向量存储检索。查看输入并尝试推理潜在的语义意图 / 含义。\n",
    "# Prompt\n",
    "system = \"\"\"You a question re-writer that converts an input question to a better version that is optimized \\n\n",
    "     for vectorstore retrieval. Look at the input and try to reason about the underlying semantic intent / meaning.\"\"\"\n",
    "re_write_prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", system),\n",
    "        (\n",
    "            \"human\",\n",
    "            \"Here is the initial question: \\n\\n {question} \\n Formulate an improved question.\",\n",
    "        ),\n",
    "    ]\n",
    ")\n",
    "\n",
    "question_rewriter = re_write_prompt | llm | StrOutputParser()\n",
    "question_rewriter.invoke({\"question\": question})"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [],
   "source": [
    "from customize.bocha_web_search import get_bocha_tool\n",
    "web_search_tool = get_bocha_tool()"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "\n",
    "class GraphState(TypedDict):\n",
    "    \"\"\"\n",
    "    Represents the state of our graph.\n",
    "\n",
    "    Attributes:\n",
    "        question: question\n",
    "        generation: LLM generation\n",
    "        documents: list of documents\n",
    "    \"\"\"\n",
    "\n",
    "    question: str\n",
    "    generation: str\n",
    "    documents: List[str]\n"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "outputs": [],
   "source": [
    "from langchain.schema import Document\n",
    "\n",
    "\n",
    "def retrieve(state):\n",
    "    \"\"\"\n",
    "    Retrieve documents\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        state (dict): New key added to state, documents, that contains retrieved documents\n",
    "    \"\"\"\n",
    "    print(\"---RETRIEVE---\")\n",
    "    question = state[\"question\"]\n",
    "\n",
    "    # Retrieval\n",
    "    documents = retriever.invoke(question)\n",
    "    return {\"documents\": documents, \"question\": question}\n",
    "\n",
    "\n",
    "def generate(state):\n",
    "    \"\"\"\n",
    "    Generate answer\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        state (dict): New key added to state, generation, that contains LLM generation\n",
    "    \"\"\"\n",
    "    print(\"---GENERATE---\")\n",
    "    question = state[\"question\"]\n",
    "    documents = state[\"documents\"]\n",
    "\n",
    "    # RAG generation\n",
    "    generation = rag_chain.invoke({\"context\": documents, \"question\": question})\n",
    "    return {\"documents\": documents, \"question\": question, \"generation\": generation}\n",
    "\n",
    "\n",
    "def grade_documents(state):\n",
    "    \"\"\"\n",
    "    Determines whether the retrieved documents are relevant to the question.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        state (dict): Updates documents key with only filtered relevant documents\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---CHECK DOCUMENT RELEVANCE TO QUESTION---\")\n",
    "    question = state[\"question\"]\n",
    "    documents = state[\"documents\"]\n",
    "\n",
    "    # Score each doc\n",
    "    filtered_docs = []\n",
    "    for d in documents:\n",
    "        score = retrieval_grader.invoke(\n",
    "            {\"question\": question, \"document\": d.page_content}\n",
    "        )\n",
    "        grade = score.binary_score\n",
    "        if grade == \"yes\":\n",
    "            print(\"---GRADE: DOCUMENT RELEVANT---\")\n",
    "            filtered_docs.append(d)\n",
    "        else:\n",
    "            print(\"---GRADE: DOCUMENT NOT RELEVANT---\")\n",
    "            continue\n",
    "    return {\"documents\": filtered_docs, \"question\": question}\n",
    "\n",
    "\n",
    "def transform_query(state):\n",
    "    \"\"\"\n",
    "    Transform the query to produce a better question.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        state (dict): Updates question key with a re-phrased question\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---TRANSFORM QUERY---\")\n",
    "    question = state[\"question\"]\n",
    "    documents = state[\"documents\"]\n",
    "\n",
    "    # Re-write question\n",
    "    better_question = question_rewriter.invoke({\"question\": question})\n",
    "    return {\"documents\": documents, \"question\": better_question}\n",
    "\n",
    "\n",
    "def web_search(state):\n",
    "    \"\"\"\n",
    "    Web search based on the re-phrased question.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        state (dict): Updates documents key with appended web results\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---WEB SEARCH---\")\n",
    "    question = state[\"question\"]\n",
    "\n",
    "    # Web search\n",
    "    docs = web_search_tool.invoke({\"query\": question})\n",
    "    #web_results = \"\\n\".join([d[\"content\"] for d in docs])\n",
    "    web_results = Document(page_content=docs)\n",
    "\n",
    "    return {\"documents\": web_results, \"question\": question}\n",
    "\n",
    "\n",
    "### Edges ###\n",
    "\n",
    "\n",
    "def route_question(state):\n",
    "    \"\"\"\n",
    "    Route question to web search or RAG.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        str: Next node to call\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---ROUTE QUESTION---\")\n",
    "    question = state[\"question\"]\n",
    "    source = question_router.invoke({\"question\": question})\n",
    "    if source.datasource == \"web_search\":\n",
    "        print(\"---ROUTE QUESTION TO WEB SEARCH---\")\n",
    "        return \"web_search\"\n",
    "    elif source.datasource == \"vectorstore\":\n",
    "        print(\"---ROUTE QUESTION TO RAG---\")\n",
    "        return \"vectorstore\"\n",
    "\n",
    "\n",
    "def decide_to_generate(state):\n",
    "    \"\"\"\n",
    "    Determines whether to generate an answer, or re-generate a question.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        str: Binary decision for next node to call\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---ASSESS GRADED DOCUMENTS---\")\n",
    "    state[\"question\"]\n",
    "    filtered_documents = state[\"documents\"]\n",
    "\n",
    "    if not filtered_documents:\n",
    "        # All documents have been filtered check_relevance\n",
    "        # We will re-generate a new query\n",
    "        print(\n",
    "            \"---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---\"\n",
    "        )\n",
    "        return \"transform_query\"\n",
    "    else:\n",
    "        # We have relevant documents, so generate answer\n",
    "        print(\"---DECISION: GENERATE---\")\n",
    "        return \"generate\"\n",
    "\n",
    "\n",
    "def grade_generation_v_documents_and_question(state):\n",
    "    \"\"\"\n",
    "    Determines whether the generation is grounded in the document and answers question.\n",
    "\n",
    "    Args:\n",
    "        state (dict): The current graph state\n",
    "\n",
    "    Returns:\n",
    "        str: Decision for next node to call\n",
    "    \"\"\"\n",
    "\n",
    "    print(\"---CHECK HALLUCINATIONS---\")\n",
    "    question = state[\"question\"]\n",
    "    documents = state[\"documents\"]\n",
    "    generation = state[\"generation\"]\n",
    "\n",
    "    score = hallucination_grader.invoke(\n",
    "        {\"documents\": documents, \"generation\": generation}\n",
    "    )\n",
    "    grade = score.binary_score\n",
    "\n",
    "    # Check hallucination\n",
    "    if grade == \"yes\":\n",
    "        print(\"---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\")\n",
    "        # Check question-answering\n",
    "        print(\"---GRADE GENERATION vs QUESTION---\")\n",
    "        score = answer_grader.invoke({\"question\": question, \"generation\": generation})\n",
    "        grade = score.binary_score\n",
    "        if grade == \"yes\":\n",
    "            print(\"---DECISION: GENERATION ADDRESSES QUESTION---\")\n",
    "            return \"useful\"\n",
    "        else:\n",
    "            print(\"---DECISION: GENERATION DOES NOT ADDRESS QUESTION---\")\n",
    "            return \"not useful\"\n",
    "    else:\n",
    "        pprint(\"---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---\")\n",
    "        return \"not supported\""
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "outputs": [],
   "source": [
    "from langgraph.graph import END, StateGraph, START\n",
    "\n",
    "workflow = StateGraph(GraphState)\n",
    "\n",
    "# Define the nodes\n",
    "workflow.add_node(\"web_search\", web_search)  # web search\n",
    "workflow.add_node(\"retrieve\", retrieve)  # retrieve\n",
    "workflow.add_node(\"grade_documents\", grade_documents)  # grade documents\n",
    "workflow.add_node(\"generate\", generate)  # generatae\n",
    "workflow.add_node(\"transform_query\", transform_query)  # transform_query\n",
    "\n",
    "# Build graph\n",
    "workflow.add_conditional_edges(\n",
    "    START,\n",
    "    route_question,\n",
    "    {\n",
    "        \"web_search\": \"web_search\",\n",
    "        \"vectorstore\": \"retrieve\",\n",
    "    },\n",
    ")\n",
    "workflow.add_edge(\"web_search\", \"generate\")\n",
    "workflow.add_edge(\"retrieve\", \"grade_documents\")\n",
    "workflow.add_conditional_edges(\n",
    "    \"grade_documents\",\n",
    "    decide_to_generate,\n",
    "    {\n",
    "        \"transform_query\": \"transform_query\",\n",
    "        \"generate\": \"generate\",\n",
    "    },\n",
    ")\n",
    "workflow.add_edge(\"transform_query\", \"retrieve\")\n",
    "workflow.add_conditional_edges(\n",
    "    \"generate\",\n",
    "    grade_generation_v_documents_and_question,\n",
    "    {\n",
    "        \"not supported\": \"generate\",\n",
    "        \"useful\": END,\n",
    "        \"not useful\": \"transform_query\",\n",
    "    },\n",
    ")\n",
    "\n",
    "# Compile\n",
    "app = workflow.compile()"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [
    {
     "data": {
      "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAJSAikDASIAAhEBAxEB/8QAHQABAQADAQEBAQEAAAAAAAAAAAYEBQcIAwIBCf/EAF0QAAEEAQIEAQQKCRAIBQMFAQEAAgMEBQYRBxITITEUFSJBCBYXMkJRVmGRlCNTVFVik9HS0yUzNDU2N1JjZHF1gaOz1OEkQ3N0obG0wXKSlbLDCSaCRVdloqTw/8QAGwEBAQADAQEBAAAAAAAAAAAAAAECAwQFBgf/xAA4EQEAAQIBCQUGBQUBAQAAAAAAAQIRAxIUIVFSYZGh0QQxQWJxEyMzscHSBTJCkvAVIoHC4bLx/9oADAMBAAIRAxEAPwD/AFTREQEREBERAREQEREBERAREQEREBERAREQEREBERARFps5mZ6s0OPx0LbOVsgljZN+nAweMshHflHgGju47AbDmc3Kmma5tB3tvJI2Jhe9wYwdy5x2AWtdqjDMcWuy9FpHiDZZ+Va5mgsbaeJsyHaht7k9TIgPjbv6mRbcjQPVsN/jJPdbBulMIxoa3D0GtHYAVWbD/gt1sGO+Zn+fzUug9tWE+/FD60z8qe2rCffih9aZ+Vf32rYX70UPqzPyJ7VsL96KH1Zn5E9zv5Lofz21YT78UPrTPyp7asJ9+KH1pn5V/fathfvRQ+rM/IntWwv3oofVmfkT3O/kaH89tWE+/FD60z8qe2rCffih9aZ+Vf32rYX70UPqzPyJ7VsL96KH1Zn5E9zv5Gh9qmcx1+TkrZCrYf8AwYpmuP0ArOWltaJ09dZyWMFjZm7bDnqRnb+bt2WDJh7mlGmzh32b1FgBkxE0nUIaPEwPd6Qd+A5xadthyb7pkYdWiidO/r/PVNCoRY9C/XylKG3VkEteZocx4BG4/mPcH4we49ayFomJibSgiIoCIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICmNE7ZLzrm37OmvW5IWO77iCF7oo2/zbh7/AOeQqnUxw9b5NgZqDtxLRu2a7wRt/rXOYf62OYf610U6MKqY79HDT9bL4KdFptUaz0/oehHe1Hncbp+lJKIWWcpcjrRvkIJDA55ALtmuO3jsD8SmR7IPha4OI4laQIaNyRnqvYeH2z51zo3fEXX+J4XaLymqM26YY3Hsa6QVo+pK8ue1jGMb63Oe5rR4Dc9yB3XLOJ3sh85pnQuEzmL0JqGpcualo4afH5apAycRySxh5YPKAwl7X8jHBxbz++2AJFDq3ifobiJpbLYDT97S3E3I265aNLV85UL70e46gHpOA2bu7cjxaO48Rymvwk4jzcJclVbi7EbsZqvH53TelsxmGW7MNStLDI6q63zOaOZzJOQF7g0FoLviDtOqOL1jS+JxFx3D/WWTnvwPnko4yjDPNSDdt2zkTcgd6XZrHuLtjtvsVpsj7JnTELND+acbm9TSazoz38PDiKrHPlbCIy9j+o9nTdtL8LZo5HbkEDeM4kaS1fxB1dprPZvhtNqXTwxU8DtH3MzWZHQyBn9GzYHOY5mmIAAt6hZudmklYPBLg3rHR1/gwzMYVlSPSmIzuOyM0VqKSNj5p4DXczZ3M5sjWOI9Hdu2zg0oLHSXHTP5/jtqHRtjRWYr4mnRx08dgx1g6m6dsznvsnyg7tPI1jRG1xBY/cbbE9sXFb+P1Hw+4+6g1bHgmZTSGoMXj697K+cIKwxHkr5+pLM2VzS6Ppzc+7Nz6BBHrVXH7ILhdK9rGcSdIPe4gNa3O1SSfiH2RBfooBnsguFsr2sZxJ0g97js1rc7VJJ+IfZFfoJjT+2L1XncQzZtd4jycLBv6JmdIJR/XJG55+eQqnUxj2+V8Q8xYbv06tGtUJI7dQukkcN/Xs10Z/8AyVOujH/NE7o+ULIiIudBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBTmUqz4HLy5unA+1Xna1mRqwtLpCGghk0bR75zQdnNHdzQOXdzGsfRE7Dc9gp2bVpydd403XjzU8lM2atoyFmPkPPyNYbDWvG5IcTyNeQGkkd2750V5E7lbapboZ6myxXlgv1XH0XsIe3cdj/WPD5l9PNtT7lh/Fj8imBw6hsZq/lrOQuRW7bYgI8dJ5KyEtZs9wMYDpS8nc9UvADWBobsScg6Im8G6ozzQPV5REf8AiY91tycKe6q3rHT/AIuhRRUq8L+eOCKN38JrACvspb2kT/KnPfj4v0Se0if5U578fF+iT2eHt8pLRrVKKW9pE/ypz34+L9EntIn+VOe/Hxfok9nh7fKS0a1Q5oe0tcAWkbEH1rH821PuWH8WPyKf9pE/ypz34+L9EntIn+VOe/Hxfok9nh7fKS0a1AMbUB/YsP4sfkWvzWo20ZfIKLG381I3eKk1+3Lv4SSnv04x63Eb9tmhziGnA9ook2FnP521HtsWG8Ytx/PEGH6CtziMFQwNd0NCrHWY48zy0buefDdzj3cfnJJS2FRpvlco6/zvTRD56fwrcHQMJl8osyyOns2C3lM0rju52252HqA3OzQ0b9ls1rMjh5rmSp3YcnbpOrMlYa8RaYJ+doAMrC0k8pAcC0tO+43IJB11fI6gxMVePK0YMq2OlJLayOKaYuaZh7MZVe5zgHt8NpHkHsfU46aqpqmap70UiLVYvVOLzFiOrXuMF99SO8cfODDbjgk35HyQPAkjBII9Jo2LXA9wQtqsQREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQERajM6kgxTpqsEZyWYFV9uHE1pIxYnY1zW7tD3NaBzOaOZxDRv3IQbdaEauhv2BFhYDnOlkDj7stSaMR0XNbzSGQucN+XcN5WBzuZwBAAcW/ifTtnPusNzlhsuPkdXlhxtbniELo/ScJJGu3mDn+IIa0ta0Fp9IuoAA0bAAevsgnqembV2Whcz183b1XrgQUjJXpuEhIAfDzu6paz0d3kjfmcGtJAG/hhjrQxwwxtiijaGMjYAGtaBsAAPABftEBERAREQEREBERAREQEREGvzOAx+oKU9XIVWWIZ4nQP33a7kJBIDhs5vcA7gg7gHxAWst4nN4uO7NhciLsjmV2Vsdl37wRch2kIma0y7vb4ueZNnAHbuQaNEGhk1jToWZ4ssx+DY25HRr2Mg5jIbj5B9j6Tw4g8x9ENds7m7bd2775fmSNsrC17Q9p8WuG4K0cGm5sRbifiLrq1WS7Lbu1bRfYbN1Bu4Rlz94dn7OAbu3u8cvpbtDfItJhNURZJ9aldh80Z2WsbT8RYlY6ZjA/kc5paSHtDtvSb4c7N9i4BbtAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREGpuZouyzcXj3VLN+IRT3IJbBY+vWeXtbLyhriS50bw0HlDuR/peiV9MDgodP4+KtHNYuSNbtJcuymWeY8znEvefwnuIaNmt32aGgADW6Hux5Wlk70eTiyzZMpcg60VTyfpdCd8BgI8XmN0TmF598Wkj0SFSICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiDBzGIhzWPsVZXzQdWN0YsVpDHNFvt6THju0ggHf4wFhwZmWhkhQy7q8DrM5jxszJD/pbRGHlrgWgMlG0noAu5ms5wffNZuljZGn5xoWKvXmqmaNzBPWfyyR7jbmafUR4jsUGSi1en7tizXngt17cVinMazprTWDykAAiZpZ6Ja4EHsBseYbDbZbRAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERARFGP1fl8sDPg8fSfjyT0rV+w9h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+ZuHfxPb5/X49vBdAQEREBERAREQEREBERAREQEREBERAREQEREBERAU7w7p+QaIw1fzfVxXTrtb5FRm60MP4LH/AAh86olO8O6fkGiMNX831cV067W+RUZutDD+Cx/wh86CiREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQc94FlrtE5EtO49s2ofFob3883d+w/5+vxXQlAcEA8aLyHUMjne2XUHeUbHbzxc2/q222+bZX6AiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICnOHdTyDRGGr+QVcX067W+R0ZutDD+Cx/wAIfOqNTnDur5DofCwCjTxojrtb5Jj5utBF+Cx/wh86CjREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEWHlczQwVUWcjdr0K5cGCWzK2NpcfBoJPcn4lovdU0d8qMT9cj/ACrbTg4lcXopmf8AC2mVSilvdU0d8qMT9cj/ACp7qmjvlRifrkf5Vnm2NsTwlcmdSpWp1Lq3B6Mx7b+oMzj8FRdIIhayVpleIvIJDeZ5A3IB7fMVrPdU0d8qMT9cj/KojjU7h/xm4X6g0hkNUYZrMjWcyGZ1th6E49KKTx39F4aTt4jcetM2xtieEmTOp/fY66/0rqHT+SxeG1JiMnkW5zPXXUqV6KWYQvy9pzZSxr3HkcJGEO8CHtPbcBdfXgH/AOnXw1w/CGPVGq9XZXH4vUNuR2Jq1rFpjXtrMe10j9t+7ZHtZsfWI9+4IXtX3VNHfKjE/XI/ypm2NsTwkyZ1KlFLe6po75UYn65H+VPdU0d8qMT9cj/KmbY2xPCTJnUqUUt7qmjvlRifrkf5Vm4nXGnc9abWx2cx96y7cthgsse92w3OwB3Ow8fiUnAxaYvNE29JS0t4iItCCIiAiIgIiICIiAiIgIiICIiAiIgKd4d1fItEYaDyKljunXa3yTHS9WvF+Cx/wh86olO8PKpo6Iw0BoU8WY67W+R0JerBF+Cx/wAIfOgokREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREEHByZTWuobNgCWbHTx0axeN+jGa8MzuX4i50vpEbE8rQdw1u25Wkw37q9a/0pF/0N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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(app.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---ROUTE QUESTION---\n",
      "---ROUTE QUESTION TO WEB SEARCH---\n",
      "---WEB SEARCH---\n",
      "\"Node 'web_search':\"\n",
      "'\\n---\\n'\n",
      "---GENERATE---\n",
      "---CHECK HALLUCINATIONS---\n",
      "---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---\n",
      "---GRADE GENERATION vs QUESTION---\n",
      "---DECISION: GENERATION ADDRESSES QUESTION---\n",
      "\"Node 'generate':\"\n",
      "'\\n---\\n'\n",
      "'根据当前情况，广东队最有可能夺冠。从阵容和实力来看，广东队仍然是冠军的最大热门，尤其是他们的外援莫兰德表现突出。'\n"
     ]
    }
   ],
   "source": [
    "from pprint import pprint\n",
    "\n",
    "# Run\n",
    "inputs = {\n",
    "    #\"question\": \"What player at the Bears expected to draft first in the 2024 NFL draft?\"\n",
    "    \"question\": \"根据2024cba俱乐部杯比赛情况，哪个球队最有可能夺冠?\"\n",
    "}\n",
    "for output in app.stream(inputs):\n",
    "    for key, value in output.items():\n",
    "        # Node\n",
    "        pprint(f\"Node '{key}':\")\n",
    "        # Optional: print full state at each node\n",
    "        # pprint.pprint(value[\"keys\"], indent=2, width=80, depth=None)\n",
    "    pprint(\"\\n---\\n\")\n",
    "\n",
    "# Final generation\n",
    "pprint(value[\"generation\"])"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
